/*
 * C1.1.0.c
 *
 *  Created on: 2013/01/09
 *      Author: tuwei
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rule_info.h"
#include "core.h"
#include "trace.h"
#include "TraceAst.h"

#define MAX_PATH	260

static const PSZ RuleID = "C1.4.4";

static const PSZ MESSAGE_ENG = "[%s] are defined in the header file.";
static const PSZ MESSAGE_JPN = "[%s]がヘッダファイル内で定義されている。";
static const PSZ MESSAGE_CHS = "[%s]被定义在头文件内。";

static const U2 RANK = MSG_RANK_ERR;

static PRULEOUT pRuleHead;
static PRULEOUT pRuleTail;

static INT RuleCheck(AST index, AST* codeScope, CHAR* buffer, INT bufLen);
static BOOL IsInHeader(AST codeScope);
static INT SetErrorMessage(AST symbol, AST* codeScope, CHAR* buffer, INT bufLen);

/******************************************************************
 * main logic
 *******************************************************************/
static S4 RuleMain(U2 kind, AST index, U4 event) {
	INT result = RULE_RESULT_OK;
	PRULEOUT newNode = NULL;
	PRULEOUT tmpNode = NULL;
	PRULEOUT tmpNode1 = NULL;
	
	AST  codeScp = 0;
	CHAR szMessage[MAX_PATH];

	if (kind != TR_KIND_DECLARATOR){
		return EXIT_SUCCESS;
	}

	result = RuleCheck(index, &codeScp, szMessage, MAX_PATH);
	
	if(result == RULE_RESULT_OK){
		return EXIT_SUCCESS;
	}
	
	/*re-checked?*/
	tmpNode = pRuleHead;
	while (tmpNode != NULL ) {
		if (tmpNode->indexInfo == index) {
			if (result > RULE_RESULT_OK) {
				result = RULE_RESULT_OK;
			} else {
				if (tmpNode == pRuleHead) {
					pRuleHead = pRuleHead->next;
					free(tmpNode);
				} else if (tmpNode == pRuleTail) {
					tmpNode1->next = NULL;
					pRuleTail = tmpNode1;
					free(tmpNode);
				} else {
					tmpNode1->next = tmpNode->next;
					free(tmpNode);
				}
			}
			break;
		}
		tmpNode1 = tmpNode;
		tmpNode = tmpNode->next;
	}

	/*make result struct*/
	if (result > RULE_RESULT_OK) {
		newNode = (PRULEOUT) malloc(sizeof(RULEOUT));
		if (pRuleHead == NULL ) {
			pRuleHead = newNode;
		}

		newNode->indexInfo = index;
		newNode->fileIndex = CCodeScope.getFileIndex(codeScp);
		newNode->beginLine = CCodeScope.getBeginLine(codeScp);
		newNode->beginCol = CCodeScope.getBeginLineOffset(codeScp);
		newNode->endLine = CCodeScope.getEndLine(codeScp);
		newNode->endCol = CCodeScope.getEndLineOffset(codeScp);
		newNode->ruleID = RuleID;
		newNode->errRank = RANK;

		//TODO
		sprintf(newNode->msgBuf_eng, MESSAGE_ENG, szMessage);
		sprintf(newNode->msgBuf_chs, MESSAGE_CHS, szMessage);
		sprintf(newNode->msgBuf_jpn, MESSAGE_JPN, szMessage);

		newNode->rfileIndex = 0;
		newNode->rlocLine = 0;
		newNode->next = NULL;

		if (pRuleTail != NULL ) {
			pRuleTail->next = newNode;
		}

		pRuleTail = newNode;
	}

	return EXIT_SUCCESS;
}

VOID C1_4_4_register( VOID) {
	PRULEMAN * pCurRule = &stRuleManager;
	PRULEMAN newRuleMan = (PRULEMAN) malloc(sizeof(RULEMAN));
	memset(newRuleMan, 0, sizeof(RULEMAN));
	newRuleMan->ruleID = RuleID;
	newRuleMan->ruleSwitch = OFF;
	newRuleMan->headOut = &pRuleHead;
	newRuleMan->tailOut = &pRuleTail;
	newRuleMan->ruleMain_ast = RuleMain;
	if (stRuleManager == NULL ) {
		stRuleManager = newRuleMan;
	} else {
		while ((*pCurRule)->nextRule != NULL ) {
			pCurRule = &((*pCurRule)->nextRule);
		}
		(*pCurRule)->nextRule = newRuleMan;
	}
}

INT RuleCheck(AST index, AST* codeScope, CHAR* buffer, INT bufLen)
{
	/*
	 * 1. The function defined in header.
	 */
	if (CDeclarator.isFunctionDeclarator(index)){
		AST childDecl = CDeclarator.getChildDeclarator(index);
		if (childDecl == 0 || !CDeclarator.isSymbolDeclarator(childDecl)){
			return RULE_RESULT_OK;
		}

		AST symbol = CDeclarator.symbol_getSymbol(childDecl);
		if (symbol != 0){
			if ( CSymbol.isFunction(symbol)
				&& CSymbol.getFunctionBody(symbol) != 0){
				return SetErrorMessage(symbol, codeScope, buffer, bufLen);
			}
		}
	}
	else
	{
		/*
		 * 2.The var without 'extern ' in header.
		 */
		if (CDeclarator.isSymbolDeclarator(index)){
			AST symbol = CDeclarator.symbol_getSymbol(index);
			if (symbol == 0 || CSymbol.isFunction(symbol)){
				return RULE_RESULT_OK;
			}

			AST decl = CDeclarator.getDeclaration(index);
			if (decl == 0 || !CDeclaration.isOrdinaryDeclaration(decl)){
				return RULE_RESULT_OK;
			}

			AST specifier = CDeclaration.getTypeSpecifier(decl);
			if (CTypeSpecifier.getExternNum(specifier) == 0){
				return SetErrorMessage(symbol, codeScope, buffer, bufLen);
			}
		}
		/*
		 * 3. The var with 'extern' but be initialized by value.
		 */
		else if (CDeclarator.isInitialExpDeclarator(index))
		{
			AST child = CDeclarator.getChildDeclarator(index);
			if (child == 0 || !CDeclarator.isSymbolDeclarator(child)){
				return RULE_RESULT_OK;
			}

			AST symbol = CDeclarator.symbol_getSymbol(child);
			if (symbol == 0){
				return RULE_RESULT_OK;
			}

			return SetErrorMessage(symbol, codeScope, buffer, bufLen);
		}
	}

	return RULE_RESULT_OK;
}

INT SetErrorMessage(AST symbol, AST* codeScope, CHAR* buffer, INT bufLen)
{
	AST scope = CSymbol.getCodeScope(symbol);
	if (scope == 0 || !IsInHeader(scope)){
		return RULE_RESULT_OK;
	}

	PSZ p = CSymbol.getName(symbol);
	if (p != NULL && *p != '\0' ){
		strncpy(buffer, p, bufLen - 1);
	}

	if (codeScope != NULL){
		*codeScope = scope;
	}

	return RULE_RESULT_NG;
}

BOOL IsInHeader(AST codeScope)
{
	CHAR szPath[MAX_PATH];
	if (CCodeScope.getFilePath(codeScope, szPath, MAX_PATH) == 0){
		return FALSE;
	}

	PSZ p =strrchr(szPath, '.');
	if (p == NULL || strcmp(p, ".h") != 0){
		return FALSE;
	}

	return TRUE;
}


